﻿#include "rec_read_base.h"
#include "rec_data_io.h"
#include "cross_com.h"

bool CRecReadBase::MIX_AUDIO  = true;
CRecReadBase::CRecReadBase(int clientID,int channel)
{
	m_curPos     = 0;
	m_pIdxBuf    = NULL;

	m_dataOffset = STFS_REC_DATA_OFFSET;
	m_pIdxBuf    = new STFS_REC_IDX[(STFS_REC_DATA_OFFSET - STFS_REC_IDX_OFFSET)/REC_INDEX_LEN];
	m_gpsBufSz   = (STFS_REC_DATA_OFFSET - STFS_REC_IDX_OFFSET)/2;
	m_pGpsBuf    = new STFS_GPS_IDX[m_gpsBufSz/REC_INDEX_LEN];
	m_gpsNum     = 0;
	m_fileStatus = STFS_REC_WRITTING;

	m_playMode   = REPLAY_NORMAL;
    m_pFIFO      = NULL;
	m_speed      = 1;
	m_stepIFrame = 1;
	m_version    = 0;
	m_fileLen    = 0;

	m_channel    = channel;
	m_clientID   = clientID;
	m_startTime	 = 0;
	m_endTime	 = 0;

	m_recOffset	 = 512;
	m_idxOffset  = 0;
	m_fileIdx	 = -1;
	m_nBufSize   = SIZE_4MB;
	m_curTime    = 0;
	m_bDataEnd   = true;
	m_clientID   = 0;
	m_segIdx     = -1;
	m_noReadCount = 0;
	m_bDownload    = false;
	m_fileStartLBA = 0;
	m_bLocalBackup = false;
	m_headOffset  = 0;
	m_headBuf     = new uchar_t[HEAD_BUF_SIZE];
	memset(m_headBuf,0,HEAD_BUF_SIZE);
	m_pFIFO       = new CReplayFrmBuf();
	m_lastTime = 0;
	m_firtFrmNo = 0;
	m_lastReadTime = 0;
	m_readThdID = PUB_CREATE_THREAD_FAIL;
	m_bComFile = false;
	m_pFile = NULL;
	m_frameRate = 30;
	m_lastfrmNo = 0;
};

CRecReadBase::~CRecReadBase()
{
	if(m_pFile)
	{
		delete m_pFile;
	}
	if(m_pIdxBuf)
	{
		delete [] m_pIdxBuf;
	}

	if( 0 == m_clientID)
	{
		m_pFIFO = NULL;
	}
	if(NULL != m_headBuf)
	{
		delete [] m_headBuf;
	}
}

int  CRecReadBase::OpenSerialFile(const char * dev,uint32_t fileIndex)
{
	int ret = 0;
	uint32_t fileSize = 0;
	if( NULL == dev)
	{
		TRACE_ERR("No device %s .\n", dev);
		return ERR_NO_DEVICE;
	}
	bool bRaw = false;

	bRaw         = CSerialFileBase::IsRawDevice(dev);
	m_bRawDev    = bRaw;
#if 0
	fileSize     = g_pFileMangerArr.GetRecFileSize();
	uint64_t fileLBA = g_pFileMangerArr.GetAssignFileLBA(dev,fileIndex);
	if(NULL == m_pFile)
	{
		m_pFile = g_serialFileFac.CreateSerialFile(dev);
	}
	else
	{
		if(m_bRawDev != bRaw)
		{
			m_pFile = g_serialFileFac.CreateSerialFile(dev);
		}
	}

	if(bRaw)
	{
		CSCSI_Dev* pDev = g_scsiDevFac.CreateScsiDev(dev);
		if( NULL == pDev)
		{
			TRACE_ERR("No device %s .\n", dev);
			return ERR_NO_DEVICE;
		}

		ret = m_pFile->Open(pDev,fileLBA,fileSize,SCSI_RDWR);
	}
	else
	{
		char tmpPath[40] = {0};
		char filePath[60] = {0};

		GetFileDestPath(tmpPath,dev);
#ifdef _WIN32
		strcat(tmpPath,"Record\\");
#else
		strcat(tmpPath,"Record/");
#endif
		sprintf(filePath,"%s%05d.asf",tmpPath,fileIndex);
		if( -1 == access(filePath ,0))
		{
			GetFileDestPath(tmpPath,dev);
			sprintf(filePath,"%s%05d.asf",tmpPath,fileIndex);
			if( -1 == access(filePath,0))
			{
				return ERR_NO_FILE;
			}
		}
		ret = m_pFile->Open(filePath,fileLBA,fileSize,SCSI_RDWR);
	}
#endif

	m_fileLen        = fileSize;
	int frmIndexSize = STFS_REC_DATA_OFFSET;
	ret = m_frmIdx.Open(frmIndexSize,STFS_REC_IDX_OFFSET,m_pFile->get_sec_size());

	return 0;
}

void CRecReadBase::StartDataIO()
{
	//加入到读取数据的队列中
	m_fileStartLBA = GetLoopLBA();
	TRACE("Dev %s Start Data IO  LBA %lld .\n",m_pFile->get_device_name(),m_fileStartLBA);
	g_rec_dataIO.AddReaderTask(m_pFile->get_device_name(), m_fileStartLBA,this);

}

void CRecReadBase::StartReadFile()
{
	m_readThdID = Cross_CreateThread((cross_start_routine) ReadFileThd, (void *)this, NULL,SIZE_128KB);
}

int   CRecReadBase::InitFIFOBuf(bool bClearBuf)
{
	int ret = 0;
	if(NULL == m_pFIFO)
	{
		TRACE_ERR("Get Frame FIFO clientID %d bDownload %d .\n",m_clientID, m_bDownload);
		m_pFIFO         = new CReplayFrmBuf();
	}
	if(!m_pFIFO->IsOpen()) //已经打开的情况下不需要再次打开,回放缓存可以调节
	{
		ret = m_pFIFO->Open(m_channel,SIZE_MB(1)+SIZE_KB(512));
	}
	if(bClearBuf)
	{
		m_pFIFO->reset(m_curPos);
	}

	return ret;
}

int  CRecReadBase::OpenByFileInfo(REC_FILE_INFO & fInfo,bool bUseFIFO,int64_t startTime,bool bClearBuf)
{
	int ret = 0;
// 	if( NULL == g_pIndexSet)
// 	{
// 		g_pIndexSet = g_pFileMangerArr.GetIndexSet();
// 	}
// 	char * dev = g_pFileMangerArr->GetIndexDiskName(fInfo.diskIndex);
	char * dev = NULL;
	ret = OpenSerialFile(dev,fInfo.indexNo);
	if(ret < 0)
	{
		TRACE("Open fileIndex %d ret %d \n",fInfo.indexNo,ret);
		return ret;
	}

	//已经打开当前文件块录像，并且已经录像完成
	//读取录像信息
	if(fInfo.endOffset > STFS_REC_DATA_OFFSET)
	{
	    m_fileLen = fInfo.endOffset;
	}
    else
    {
        m_fileLen = STFS_RECORD_SPACE;
    }

	if( (m_fileIdx != fInfo.indexNo)     ||
		(m_fileStatus == STFS_REC_WRITTING))
	{
		ret = ReadRecInfo();
        if(ret < 0)
		{
		   TRACE("ClientID %02d CH%02d fileIdx %d -%d  Read record info ret %d \n",m_clientID,m_channel,fInfo.indexNo,fInfo.segIndex,ret);
			Close();
			return ret;
		}
	}

	m_startTime = fInfo.tmStart;
	if(fInfo.tmEnd  < STFS_REC_MAX_TIMEMS)
	{
		m_endTime   = fInfo.tmEnd;
	}

	if(m_fileLen == STFS_RECORD_SPACE)
	{
		GetEndOffset(m_fileLen,fInfo.tmEnd);
	}
	//定位在相关的时间表
	if(startTime < fInfo.tmStart)
	{
	    startTime = fInfo.tmStart;
	}

	if( m_fileIdx   == fInfo.indexNo &&
		!bClearBuf)
	{
	    if(NULL != m_pFIFO)
        {
             m_curTime = m_pFIFO->GetNextFrmTime()+30;
        }
		TRACE("ClientID%02d CH%02d fileIdx%d-%d Connutinue to read .Cur-pos %d \n",m_clientID,m_channel,fInfo.indexNo,fInfo.segIndex,m_curPos);
	}
	else
	{
		m_curPos    = m_dataOffset;
		m_curIndex  = 0;
		//存在时间的话，先去定位相应的关键帧位置　
		switch (m_playMode)
		{
		case	REPLAY_ONEFRAME:					//单帧播放
		case    REPLAY_NORMAL:
		case	REPLAY_SLOW:						//慢进 2, 4, 8, 16, 32
		case	REPLAY_FAST:						//快进 2, 4, 8, 16, 32
			break;
		case	REPLAY_REBACK:						//回退4, 8, 16, 32
			m_curIndex  = m_maxIndex - 1;
			break;
		default:
			TRACE_ERR("Mode is error. speed %d .mode %d \n",m_speed,m_playMode);
			break;
		}
	}
	TRACE("\n------CH %02d file fileIdx%d-%d- startTime %lld  fileLen %d mode %d speed %d\n",
		m_channel, m_fileIdx,fInfo.segIndex,startTime,m_fileLen,m_playMode,m_speed);
	if(startTime != 0)
	{
		ret = SeekToTime(startTime,bClearBuf);
	}
	else
	{
		ret = SeekToTime(fInfo.tmStart,bClearBuf);
	}
    if(ret < 0 )
    {
        Close();
        TRACE_ERR("CH %02d file fileIdx%d- Seek fail.\n",m_channel, m_fileIdx);
        return ret;
    }

	m_fileIdx  = fInfo.indexNo;
	m_segIdx   = fInfo.segIndex;
	m_curTime  = fInfo.tmStart;
	m_bDataEnd = false;
	m_channel  = fInfo.channel;
	m_firtFrmNo = 0;
	Seek(m_curPos);

	ret = InitFIFOBuf(bClearBuf);
	if(ret < 0)
	{
		return ret;
	}
	//定位到第一帧数据位置
	TRACE("\n------CH %02d file fileIdx%d-%d- Open OK. maxIdx %d curoffset %d fileLen %d mode %d speed %d\n",
		       m_channel, m_fileIdx,fInfo.segIndex,m_maxIndex,m_curPos,m_fileLen,m_playMode,m_speed);
	PrintTimeMs(fInfo.tmStart,fInfo.tmEnd,"Open file read");
	PrintTimeMs(m_pIdxBuf[m_maxIndex - 1].tmMS,0,"Last IFrame time");

	TRACE("Start Data IO usbFIFO %d .\n",bUseFIFO);

	if(bUseFIFO)
	{
		StartDataIO();
	}
	return 0;
}

#if 0
int  CRecReadBase::Open(REC_FILE_INFO & fInfo,STFS_REC_FILEHEAD * pRecHeader)
{
	int ret = 0;

	bool	 bClearBuf = true;
	char * dev = g_pIndexSet->GetIndexDiskName(fInfo.diskIndex);
	ret = OpenSerialFile(dev,fInfo.indexNo);
	if(ret < 0)
	{
		TRACE_ERR("Open fileIndex %d ret %d \n",fInfo.indexNo,ret);
		return ret;
	}

	ret = ReadRecInfo(pRecHeader);
	if(ret < 0)
	{
		TRACE_ERR("Read record info ret %d \n",ret);
		return ret;
	}

	m_curPos = STFS_REC_DATA_OFFSET;
	m_pFile->Seek(m_curPos,SEEK_SET);
	m_curIndex    = 0;

	//存在时间的话，先去定位相应的关键帧位置　

	m_startTime = fInfo.tmStart;
	m_endTime = fInfo.tmEnd;
	SeekToTime(fInfo.tmStart,true);
	if(NULL !=m_pFIFO)
	{
		m_pFIFO->SetFileOffsset(m_curPos);
		if(m_fileIdx != fInfo.indexNo || bClearBuf)
		{
			m_pFIFO->reset(m_curPos);
		}
	}

	//定位到第一帧数据位置
	m_clientID = 0;
	m_fileIdx  = fInfo.indexNo;
	m_curTime  = fInfo.tmStart;

	m_bDataEnd = false;
	m_channel  = fInfo.channel;
	TRACE("CH %02d file index %d.Open OK. mode %d speed %d\n",m_channel, m_fileIdx,m_playMode,m_speed);

	return 0;
}
#endif
int  CRecReadBase::OpenBaseFile(const char * dev, uint32_t fileIndex,STFS_REC_FILEHEAD* pRecordHead,vector<STFS_REC_SEG> & vecSeg)
{
	if( NULL == dev)
	{
		return ERR_NO_DEVICE;
	}

	int ret = OpenSerialFile(dev,fileIndex);
	if(ret < 0)
	{
		TRACE_ERR("Open fileIndex %d ret %d \n",fileIndex,ret);
		return ret;
	}

	ret = ReadRecInfo(pRecordHead);
	if(ret < 0)
	{
		TRACE_ERR("Read record info ret %d \n",ret);
		return ret;
	}

	m_startTime = pRecordHead->startTime;

	if( STFS_REC_WRITTING == pRecordHead->writeStatus)
	{
		GetDvrCurTime(&(pRecordHead->endTime));
	}

	STFS_REC_SEG *pRecSeg = NULL;
	uchar_t * pBuf = m_frmIdx.GetBufAddr() + m_headOffset;
	for(uint32_t i = 0; i < STFS_MAX_SEG_NUM ; i++)
	{
		pRecSeg = (STFS_REC_SEG *)(pBuf + i * REC_SEG_LEN + STFS_REC_SEG_OFFSET);
		if(STFS_REC_SEG_SIG == pRecSeg->magic)
		{
			vecSeg.push_back(*pRecSeg);
			continue;
		}
	}

	if( 0== vecSeg.size())
	{
		STFS_REC_SEG tmpSeg;
		tmpSeg.tmStart = pRecordHead->startTime;//(int64_t)m_pIdxBuf[0].tmSec * STFS_1SEC_MS;
		tmpSeg.tmEnd   = pRecordHead->endTime;
		tmpSeg.idxNum = m_maxIndex;
		tmpSeg.type   = RECORD_MANUAL;
		tmpSeg.data_start  = m_pIdxBuf[0].offset;
		tmpSeg.data_endpos = m_pIdxBuf[m_maxIndex - 1].offset + m_pIdxBuf[m_maxIndex - 1].frameLen;
		tmpSeg.dataLen     = tmpSeg.data_endpos - tmpSeg.data_start;
		tmpSeg.idx_startNo = 0;
		if( 0 == tmpSeg.tmEnd)
		{
			GetDvrCurTime(&(tmpSeg.tmEnd));
		}
		vecSeg.push_back(tmpSeg);
	}
	m_fileIdx   =  fileIndex;
	return 0;
}

void CRecReadBase::Close(bool bImmediate)
{
	if( NULL == m_pFile)
	{
		return;
	}
    if(bImmediate)
	{
		g_rec_dataIO.DelReaderTask(m_pFile->get_device_name(),m_fileStartLBA,this);
	}

    TRACE_ERR("ClientID %02d CH%02d \n",m_clientID,m_channel);
	CCross_LockAuto tempLock(&m_modeLock);

	if(m_pFile->is_closed())
	{
		return;
	}

	if(m_bDownload && !bImmediate)
	{
	    TRACE_ERR("ClientID %02d %02d index %d MkEmptyFrame",m_clientID,m_channel,m_fileIdx);
	}

	m_pFile->Close();
	m_fileLen    = 0;
	m_curIndex   = 0;
	m_bDataEnd   = true;
	//m_startTime	 = 0;
	//m_endTime	 = 0;
	TRACE_ERR("ClientID %02d %02d index %d CRecReadBase::Close \n",m_clientID,m_channel,m_fileIdx);
}

void CRecReadBase::Clear()
{
	m_fileLen    = 0;
	m_curIndex   = -1;
	m_fileStatus = STFS_REC_WRITTING;

	m_playMode   = REPLAY_NORMAL;
	m_speed      = 1;
	m_stepIFrame = 1;
	m_bDataEnd   = true;
	if(NULL != m_pFIFO)
	{
		m_pFIFO->reset(0);
	}
}

int CRecReadBase::SeekToTime(int64_t time, bool bClear)
{
	if(m_maxIndex <= 0)
	{
		TRACE_ERR("No Key frame video.\n");
		return ERR_REPLAY_SEEK;
	}
	PrintTimeMs(time,0,"playback Seek time ");
	CCross_LockAuto tempLock(&m_modeLock);
	if(m_pIdxBuf[0].tmMS >= time)
	{
		m_curIndex  = 0;
		m_curPos   = m_pIdxBuf[m_curIndex].offset;
		TRACE("CH%02d Seek time to file begin offset %d GetDataSize %d \n",m_channel,m_curPos,m_pFIFO->GetDataSize());
	    PrintTimeMs(time,0,"Seek time");
		if(m_pFIFO != NULL && m_pFIFO->IsOpen())
		{
			if(bClear)
			{
				m_pFIFO->reset(m_curPos);
			}
		}

		Seek(m_curPos);
		return SEEK_RET_BEGIN;
	}

	int64_t startTime    = m_pIdxBuf[0].tmMS;
	int64_t endTime      = m_pIdxBuf[m_maxIndex-1].tmMS;
	uint32_t endIndex    =  m_maxIndex-1;
	uint32_t videoIndex  = 0;

	{
		if (time <= startTime)
		{
			videoIndex = 0;
		}
		else if ( 0 != endTime && time >= endTime)
		{
			videoIndex = endIndex;
		}
		else
		{
			SeekKeyIndex(time,videoIndex);
		}

		if (REPLAY_REBACK == m_playMode)
		{   //倒放功能
			uint32_t video1IndexTemp = videoIndex;
			while(video1IndexTemp > 0)
			{
				if( 0 != m_pIdxBuf[video1IndexTemp].tmMS &&
					m_pIdxBuf[video1IndexTemp].tmMS <= m_endTime)
				{
					break;
				}
				video1IndexTemp--;
			}
			videoIndex = video1IndexTemp;
		}
	}

	m_curIndex	= videoIndex;
	m_curPos	= m_pIdxBuf[m_curIndex].offset;

	if(0 == m_curIndex)
	{
		m_curPos = STFS_REC_DATA_OFFSET;
	}

	if((NULL != m_pFIFO) && m_pFIFO->IsOpen())
	{
		if(bClear)
		{
			m_pFIFO->reset(m_curPos);
			TRACE("Clear FIFO data size %d \n",m_pFIFO->GetDataSize());
		}
	}

	Seek(m_curPos);

	TRACE("Client%02d CH%02d Seek ok to m_curIndex %d offset %d mode %d.",m_clientID,m_channel,m_curIndex,m_curPos,m_playMode);
	PrintTimeMs(m_pIdxBuf[m_curIndex].tmMS,0,0);

	return 0;
}

int CRecReadBase::SeekKeyIndex(int64_t time,uint32_t & index)
{
	bool      bFound      = false;
	uint32_t tmSec        = (uint32_t)(time/1000);
	int  diff  = 0;

	for(int i = 0 ; i < m_maxIndex; i ++)
	{
#ifdef _DEBUG

		if(i < 8)
		{
			TRACE("NO %d ,offset %d frame len %d time %lld ",
				i,m_pIdxBuf[i].offset,m_pIdxBuf[i].frameLen,m_pIdxBuf[i].tmMS);
			PrintTimeSec(m_pIdxBuf[i].tmMS,0,"IFrame time");
		}

		if( m_pIdxBuf[i].tmMS > tmSec)
		{
			 diff  = m_pIdxBuf[i].tmMS - tmSec;
		}
		else
		{
			diff  = tmSec - m_pIdxBuf[i].tmMS;
		}

		if( diff < 16)
		{
			TRACE("NO %d ,offset %d frame len %d time %d ",
				i,m_pIdxBuf[i].offset,m_pIdxBuf[i].frameLen,m_pIdxBuf[i].tmMS);
			PrintTimeSec(m_pIdxBuf[i].tmMS,0,"IFrame time");
		}
#endif

		if( 0 != m_pIdxBuf[i].tmMS && m_pIdxBuf[i].tmMS >= time)
		{
			index = i;
			TRACE("ch%02d NO %d cur time %d index time %lld ,offset %d \n",m_channel,i,tmSec,m_pIdxBuf[i].tmMS,m_pIdxBuf[i].offset);
			PrintTimeSec(tmSec,0,"Seek time");
			PrintTimeSec(m_pIdxBuf[index].tmMS,0,"cur Time");
			bFound = true;
			break;
		}
	}

	if( bFound)
	{
		if( !m_bDownload   && //下载和备份时会
			!m_bLocalBackup)
		{
			return 0;
		}

		if( index > 0 && m_pIdxBuf[index].tmMS > (uint32_t)(m_endTime/1000))
		{
			index -= 1;
			PrintTimeSec(m_pIdxBuf[index].tmMS,0,"seek time");
			m_startTime = (int64_t)(m_pIdxBuf[index].tmMS) *1000;
		}
	}
	else
	{
		return ERR_OVER_MAX_TIMESEC;
	}

	return 0;
}

//通过数据偏移去定位最近的I帧索引
//
int CRecReadBase::GetIndexByPos(int & curIndex, uint32_t curPos, PLAYBACK_MODE mode)
{
	if(mode != REPLAY_REBACK)
	{	//正向读写数据处理
		for(int i = 0; i < m_maxIndex; i++)
		{
			if(m_pIdxBuf[i].offset >= curPos)
			{
				curIndex = i;
				return 0;
			}
		}
	}
	else
	{
		for(int i = m_maxIndex -1; i >= 0; i--)
		{
			if(m_pIdxBuf[i].offset < m_curPos)
			{
				curIndex = i;
				return 0;
			}
		}
	}

	return ERR_PARAM;
}

int CRecReadBase::SetPlayMode(PLAYBACK_MODE mode,int speed,bool bReSeek)
{
	switch (mode)
	{
	case 	REPLAY_STOP:						//停止
	case    REPLAY_PLAY:						//播放 1
	case	REPLAY_PAUSE:						//暂停
	case	REPLAY_ONEFRAME:					//单帧播放
	case    REPLAY_NORMAL:
	case	REPLAY_SLOW:						//慢进 2, 4, 8, 16, 32
	case	REPLAY_FAST:						//快进 2, 4, 8, 16, 32
	case	REPLAY_REBACK:						//回退 8, 16, 32
		break;
	default:
		TRACE_ERR("Mode is error. speed %d .mode %d \n",m_speed,mode);
		return ERR_PARAM;
	}

	if((mode == m_playMode) && (speed == m_speed))
	{
		//
	//	TRACE_ERR("Mode no changed.\n");
		return 0;
	}

	if(speed >32)
	{
		TRACE_ERR("Speed is error. speed %d .mode %d \n",m_speed,mode);
		return ERR_PARAM;
	}

	CCross_LockAuto tempLock(&m_modeLock);
	if((mode != m_playMode) || (speed != m_speed ))
	{
		//快进， ReadFIFOForward
		//快退,  ReadFIFOReback
		//需要处理进度m_curPos
		if((NULL != m_pFIFO))
		{
			m_curTime = m_lastReadTime;
			TRACE("last time %lld mode %d playmod %d \n",m_curTime,mode,m_playMode);
			PrintTimeMs(m_curTime);
         //   CROSS_ASSERT(m_curTime < STFS_REC_MAX_TIMEMS);
			if(bReSeek)
			{
				SeekToTime(m_curTime,true);
			}
		}

		Seek(m_curPos);
	}
	m_playMode	= mode;
	m_bDataEnd  = false;

	m_stepIFrame = speed / STFS_REPLAY_IFRAME_STEP;

	if(m_stepIFrame == 0)
	{
		m_stepIFrame = 1;
	}
	m_speed		= speed;

    TRACE("Speed %d mode %d step %d\n",m_speed,mode,m_stepIFrame);
	return 0;
}

int CRecReadBase::ReadGPSInfo(uint32_t gpsTm,uchar_t * gpsInfo,int gpsInfoSize)
{
	bool    bFound      = false;
    int64_t  chkTm      = (int64_t)gpsTm * STFS_1SEC_MS;
	for(int i = 0 ; i < m_maxIndex; i ++)
	{
		if(0 != m_pIdxBuf[i].tmMS && m_pIdxBuf[i].tmMS >= chkTm)
		{
		//	TRACE("ch%02d NO %d cur time %d index time %d ,offset %d \n",m_channel,i,gpsTm,m_pIdxBuf[i].tmSec,m_pIdxBuf[i].offset);
			memcpy(gpsInfo,&m_pGpsBuf[i],GPS_INDEX_LEN);
			bFound = true;
			break;
		}
	};

	if(!bFound)
	{
		memset(gpsInfo,0,gpsInfoSize);
	}
	return 0;
}

int CRecReadBase::Read_TO_FIFO(int cache_wather)
{
	int ret = 0;

	ret = m_pFIFO->GetCurWarter();
	if(ret > cache_wather)
	{
		if(STFS_READBUF_FULL == ret)
		{
			if(m_noReadCount < 3)
			{
		//		TRACE("clientID%02d CH %02d data is full. m_curIndex %d \n",m_clientID,m_channel,m_curIndex);
		//		PrintTimeMs(m_pFIFO->GetNextFrmTime(),0,"lastFrame Time");
		//		if(m_pIdxBuf[m_curIndex].time != 0)
				{
		//			PrintTimeMs(m_pIdxBuf[m_curIndex].time,0,"Cur Iframe Time");
				}
			}
			m_noReadCount++;
			return 0;
		}
		if(ret > cache_wather + 10)
		{
			m_noReadCount++;
			return 0;
		}
		if(m_noReadCount < 5)
		{
			m_noReadCount++;
			return 0;
		}
	}

	if(m_bDataEnd )
	{
		if(m_bDownload)
        {
            TRACE("CH%02d index%d -%d  Read_TO_FIFO End read file.last offset %d .\n",m_channel,m_fileIdx,m_segIdx,m_curPos);
            return ERR_DATA_AREA;
        }
		return ERR_DATA_AREA;
	}

	m_noReadCount = 0;
	m_curPos = m_pFile->get_cur_offset();
	if(ret <= 10 || m_bDownload)
	{
#ifndef _WIN32
	   TRACE("ClientID %02d CH%02d index %d start read data offset %u mode %d speed %d. step %d water %d \n",
			m_clientID,m_channel,m_fileIdx,m_pFile->get_cur_offset(),m_playMode,m_speed,m_stepIFrame,ret);
#endif
	}

	CCross_LockAuto tempLock(&m_modeLock);

	switch(m_playMode)
	{
	case    REPLAY_NORMAL:
	case	REPLAY_ONEFRAME:					//
	case	REPLAY_SLOW:						//慢放 2, 4, 8, 16, 32
        if(m_curPos >= STFS_RECORD_SPACE)
        {
            TRACE_ERR("CH%02d index%d  no leaf data curpos%d fileLen %d   End read file.\n",m_channel,m_fileIdx,m_curPos,m_fileLen);
            m_bDataEnd = true;
            return ERR_DATA_OVER_FILE;
        }

		ret = ReadFIFONormal();
		break;
	case	REPLAY_FAST:						//快进 2, 4, 8, 16, 32

		if(m_speed <= 1)
		{
			ret = ReadFIFONormal();
		}
		else
		{
			ret = ReadFIFOForward();
		}
		break;
	case	REPLAY_REBACK:						//4 8, 16, 32
		ret = ReadFIFOReback();
		break;
	default:
		ret = ReadFIFONormal();
		break;
	}

	if(ERR_BAD_SECTOR == ret)
	{
		ret = DealBadSector();
	}

	return ret;
}

//正向读取数据
int CRecReadBase::ReadRecordData()
{
	int ret = 0;
	unsigned char* pbuf = NULL;
	unsigned char* pTailBuf = NULL;
	uint32_t   leaf_size  = 0;
	uint32_t   read_size  = m_pFIFO->GetFreeSize();
	bool bForceCheck      = false;
   // TRACE_CBS("Read To FIFO curoffset %d read_size %d.\n",m_curPos,read_size);
	pbuf = m_pFIFO->GetFreeBuf(&pTailBuf,&leaf_size);
	uint32_t leaf_data = m_fileLen - m_curPos;// m_fileSize - m_cur_offset;
	if(0 == leaf_data )
    {
		m_bDataEnd = true;
		TRACE_ERR("Cur Pos %d fileLen %d   .\n",m_curPos,m_fileLen);
        return ERR_REPLAY_NODATA;
    }

	ret = CheckFrameArea(m_curPos);
	switch (ret)
	{
	case ERR_DATA_OVER_FILE:
		//m_bDataEnd = true;
		bForceCheck = true;
		TRACE("Frame data is over record segment. curpos %d curIndex %d \n",m_curPos,m_curIndex);
	case ERR_PARAM:
		TRACE("Need force check frame.curpos %d curIndex %d \n",m_curPos,m_curIndex);
		bForceCheck = true;
		break;
	case 0:
	default:
		break;
	}

	if(read_size > leaf_data)
	{
		read_size = leaf_data;
	}

    if(0 == read_size)
    {
        return 0;
    }

	if(NULL != pTailBuf)
	{
		//剩余空间分段
		if(0 != leaf_size)
		{
			if(leaf_size > read_size)
			{
				leaf_size = read_size;
			}
			ret = Read(pbuf,leaf_size);
			if(ret < 0)
			{
				TRACE_CBS("2 Read data fail.ret %d  leaf_size %d \n",ret, leaf_size);
				goto ERR_REC;
			}

			//增加下载边界判断
#if 0
			if(bForceCheck || m_bDownload)
			{
				while(((addOffset + FRM_HEAD_LEN) < leaf_size))
				{	//抽检第一帧数据
					pFrm    = (FsFrame_t*)(pbuf + addOffset);
					if(pFrm->frameTag == FSFrameTag)
					{
						if(pFrm->time <= m_endTime && pFrm->time >= m_startTime)
						{
							bCheckFrame = true;
							break;
						}
						else
						{  //已经超
							PrintTimeMs(pFrm->time,0,"Cur time");
							PrintTimeMs(m_endTime,0,"end Time");
							TRACE_ERR("!!!!! 1 ClientID %02d CH%02d index %d cur-offset %d Error time .\n",
								m_clientID,m_channel,m_fileIdx,m_pFile->get_cur_offset());
							m_pFIFO->ProductBuf(addOffset);
							goto ERR_REC;
						}
					}
					addOffset += 1;
	#ifdef _DEBUG
					//取数据缓冲中已经做了帧头检验，这里考虑忽略掉
					if(addOffset > STFS_GETFRM_BUFSIZE)
					{
						TRACE_ERR("No frame. %d \n",addOffset);
						CROSS_ASSERT(addOffset);
						goto ERR_REC;
					}
	#endif
				}
			}
#endif
			m_pFIFO->ProductBuf(leaf_size);
		}

		read_size = read_size - leaf_size;
		if(0 < read_size)
		{
			ret = Read(pTailBuf,read_size);
			if(ret < 0)
			{
				TRACE("1 Read data fail.ret %d  read_size %d \n",ret, read_size);
				goto ERR_REC;
			}

			m_pFIFO->ProductBuf(read_size);
		}
	}
	else if(read_size != 0)
	{
		ret = Read(pbuf,read_size);
		if(ret < 0 )
		{
			TRACE("3 Read data fail.ret %d  read_size %d \n",ret, read_size);
			goto ERR_REC;
		}

		m_pFIFO->ProductBuf(read_size);
	}
	m_curPos = m_pFile->get_cur_offset();

	return 0;

ERR_REC:
	TRACE_ERR("CH %02d Read To FIFO end curoffset %d  read_size %d \n",m_channel,m_curPos,read_size);
	m_bDataEnd = true;
	m_curPos = m_pFile->get_cur_offset();
	return ret;
}

int CRecReadBase::ReplayWait(int64_t curTm)
{
	uint32_t delay = 0;
	uint32_t diff  = 0;

	diff = abs(curTm - m_lastTime);
	switch (m_playMode)
	{
	case REPLAY_NORMAL:
		delay = diff;
		break;
	case REPLAY_REBACK:
		delay = diff / m_speed;
		break;
	case REPLAY_FAST:
		delay = diff / m_speed;
		break;
	case REPLAY_SLOW:
		delay = diff*m_speed;
	default:
		delay = 40;
		break;
	}

	//CROSS_ASSERT(delay < 60 *1000);
	if(delay > 1000)
	{
		delay = 1000;
	}
	CrossSleep(delay);
	m_lastTime = curTm;
	return 0;
}
//两倍速快进的思想
/*
  1. 间隔3帧数据 试过间隔2时，可能出现花屏。
  2. I帧不会跳掉
*/
int CRecReadBase::ReadFIFO2Forward()
{
	int ret = 0;

	//TRACE("Read To FIFO 2Forward curoffset %d \n",m_cur_offset);

	uint32_t read_size = m_pFIFO->GetFreeSize();
	uint32_t frmSize   = 0;
	uint32_t oneFrmLen = 0;	//单帧数据长度
	uint32_t leaf_data = m_fileLen - m_curPos;// m_fileSize - m_cur_offset;
	if(read_size > leaf_data)
	{
		read_size = leaf_data;
	}
	uchar_t * transBuf = g_rec_dataIO.GetRpBuf(m_pFile->get_device_name());
	CROSS_ASSERT(transBuf != NULL);
	uint32_t  bufSize  = g_rec_dataIO.GetRpBufSize(m_pFile->get_device_name());
	uint32_t  bufOffset= 0;
	FsFrame_t* pFrm    = NULL;
	uchar_t * frmBuf   = NULL;
	int		  step     = 0;
	int64_t  timeBak  = 0;
	uint32_t readSize = bufSize;
	//循环读取数据到FIFO缓冲当中
//	m_pFile->Lock();

	while((frmSize + FRM_HEAD_LEN)< read_size)
	{
		//读取数据到缓冲当中
		leaf_data = m_fileLen - m_curPos;
		if(leaf_data < bufSize)
		{
			readSize = leaf_data;
		}
		else
		{
			readSize = bufSize;
		}

		ret = m_pFile->ReadPos(m_curPos,transBuf,readSize);
		if(ret < 0)
		{
		   TRACE_ERR(" CH%02d pos %d Read data Error.\n",m_channel,m_curPos);
		   goto	ERR_REC;
		}

		//挑取数据
		bufOffset = 0;
		while((bufOffset + FRM_HEAD_LEN)< bufSize)
		{
		    pFrm    = (FsFrame_t*)(transBuf + bufOffset);
			if(pFrm->frameTag != FSFrameTag)
			{
				bufOffset += 1;
				while(bufOffset < bufSize)
				{
					pFrm    = (FsFrame_t*)(transBuf + bufOffset);
					if(pFrm->frameTag == FSFrameTag)
					{
						break;
					}
					bufOffset += 1;
				}
			}

			if(pFrm->frameTag != FSFrameTag)
			{
				TRACE_ERR("Error data flag %d.\n",pFrm->frameTag);
				goto ERR_REC;
			}

			frmBuf  = (uchar_t*)(transBuf + bufOffset);
			oneFrmLen = pFrm->dataLen + FRM_HEAD_LEN ;
			if((bufOffset + oneFrmLen) > bufSize)
			{
				break;
			}
			//满足两个条件即可写入到缓冲当中
			//1.间隔2帧数据
			//2.I帧数据强制写入
			if((m_curPos + oneFrmLen)> m_fileLen)
			{
				m_bDataEnd = true;
				goto FILL_END;
			}

			//如果时间超出结束时间，应该立即结束读取数据
			if(pFrm->time > m_endTime)
			{
				m_bDataEnd = true;
				goto FILL_END;
			}

			//支持MJ 快进两倍速，跳帧处理
			if( pFrm->frameType == PFrame ||
				pFrm->frameType == MFrame)
			{
				step++;
			}

			if((step ==3 ) || (pFrm->frameType == IFrame))
			{
				step = 0;
				if(pFrm->time < timeBak)
				{
					TRACE_ERR("Time Error.\n");
				}

				ret = m_pFIFO->write(frmBuf,oneFrmLen);

				if( ERR_FIFO_NOTENOUGH == ret)
				{
					//TRACE_ERR("Full data.\n");
					goto FILL_END;
				}

				timeBak = pFrm->time;
				frmSize   += oneFrmLen ;
			}
			bufOffset += oneFrmLen;
			m_curPos += oneFrmLen;

		}

	}

FILL_END:
//	TRACE("CH%02d Read To FIFO  end curoffset %d \n",m_channel,m_curPos);
	return 0;

ERR_REC:
	TRACE("ClientID %02d CH%02d Read To FIFO  end curoffset %d \n",m_clientID,m_channel,m_curPos);
	m_bDataEnd = true;
//	m_pFile->Unlock();
	return ret;
}

//带有校正偏移的函数

int CRecReadBase::GetRecHeader(STFS_REC_FILEHEAD * pRecHeader)
{
	int ret =0;

	if(m_pFile->is_closed())
	{
		return ERR_FILE_NOOPEN;
	}

	if(0 != m_frmIdx.GetBufSize())
	{  //已经读取过,则直接拷贝走头信息
		memcpy(pRecHeader,m_frmIdx.GetBufAddr(),REC_HEADINFO_LEN);
		if(STFS_REC_END_FLAG == pRecHeader->writeStatus)
		{
			return 0;
		}
	}
	ret = m_pFile->ReadPos(m_headOffset,(uchar_t*)pRecHeader,REC_HEADINFO_LEN);
	if(ret != REC_HEADINFO_LEN)
	{
		TRACE_ERR("Read Head info fail.\n");
		return ret;
	}

	m_version     = pRecHeader->version;
	if(pRecHeader->fileLen)
	{
		m_fileLen = pRecHeader->fileLen;
	}

	m_channel     = pRecHeader->channel;
	m_firstTime   = pRecHeader->startTime;
	m_endTime     = pRecHeader->endTime;

	m_idxOffset   = pRecHeader->idxOffset;
	m_recOffset   = pRecHeader->recOffset;
	m_dataOffset  = pRecHeader->dataOffset;
	m_fileStatus  = pRecHeader->writeStatus;

	//判断文件是否为录像文件
	if(STFS_REC_WRITTING == pRecHeader->writeStatus)
	{
		TRACE_ERR("File is writting.\n");
	}

	if(m_fileLen != 0)
	{
		TRACE_ERR("File length %d.\n",m_fileLen);
	//	m_pFile->SetDataArea(m_fileLen);
	}
	return 0;
}

//	STFS_REC_FILEHEAD recFileHead;
int CRecReadBase::ReadRecInfo(STFS_REC_FILEHEAD * pRecHeader)
{
	STFS_REC_FILEHEAD* pFileHd = NULL;
	static char fileMagic[8] = STFS_RECORD_MAGIC;
	int ret = 0;

	uchar_t * dataBuf = m_frmIdx.GetBufAddr();
	//一次读取文件头信息+ 录像段信息+I桢索引信息
	ret = m_pFile->ReadPos(0,dataBuf,STFS_REC_DATA_OFFSET);
	if(ret < 0)
	{
		TRACE_ERR("Read pos %d ret %d \n",0,ret);
		return ret;
	}

	uint32_t diffOffset = 0;
	bool bFound = false;
	while(diffOffset < STFS_REC_IDX_OFFSET)
	{
		pFileHd = (STFS_REC_FILEHEAD*)(dataBuf + diffOffset);
		//比较签名
		if(memcmp(pFileHd->magic,fileMagic,MAGIC_LEN) == 0)
		{
			bFound = true;
			break;
		}
		diffOffset += SIZE_HDD_BLOCK1;
	}

	if(!bFound)
	{
		TRACE("This file not record file.\n");
		return ERR_NOT_RECFILE;
	}
	m_headOffset = diffOffset;
	if(NULL != pRecHeader)
	{   //拷贝出头信息
		memcpy(pRecHeader,pFileHd,REC_HEADINFO_LEN);
	}

	m_version     = pFileHd->version;
	m_channel     = pFileHd->channel;
	m_startTime   = m_firstTime   = pFileHd->startTime;
	m_endTime     = pFileHd->endTime;
	m_idxOffset   = pFileHd->idxOffset;
	m_recOffset   = pFileHd->recOffset;
	m_dataOffset  = pFileHd->dataOffset;
	m_fileStatus  = pFileHd->writeStatus;
	m_frameRate   = pFileHd->frameRate;

	if(0 == m_fileLen)
	{
		m_fileLen = pFileHd->fileSpace;
	}
	//判断文件是否为录像文件
	if(STFS_REC_WRITTING == m_fileStatus)
	{
		TRACE_ERR("CH %d idx %d File is writting.\n",pFileHd->channel,pFileHd->indexNO);
		//正在写入的录像文件
	}

	GetRecIdxByHeadBuf();
	if(m_maxIndex <= 0 )
	{
		if(pFileHd->fileLen <= STFS_REC_DATA_OFFSET )
		{
			TRACE("Can not find index info.\n");
			return ERR_INDEX_NORECIDX;
		}
	}

	if(m_endTime == 0)
	{
		m_endTime = m_pIdxBuf[m_maxIndex - 1].tmMS;
	}

#ifdef _DEBUG
	PrintTimeMs(m_pIdxBuf[0].tmMS,m_pIdxBuf[m_maxIndex - 1].tmMS ,"Record Index diff");
#endif
	return m_maxIndex;
}

uint32_t CRecReadBase::GetRecordSize(int64_t startTime, int64_t endTime)
{
	uint32_t dataSize    = 0;
	uint32_t startOffset = m_curPos;
	uint32_t endOffset   = m_curPos;

	int i = 0;
	for(i = 0; i < m_maxIndex; i ++)
	{
		if(0 != m_pIdxBuf[i].tmMS && m_pIdxBuf[i].tmMS >= startTime)
		{
			startOffset = m_pIdxBuf[i].offset;
			break;
		}
	}
	endOffset = m_pIdxBuf[m_maxIndex - 1].offset + m_pIdxBuf[m_maxIndex - 1].frameLen;
	for(i = 0; i < m_maxIndex; i ++)
	{
		if(0 != m_pIdxBuf[i].tmMS && m_pIdxBuf[i].tmMS >= endTime)
		{
			endOffset = m_pIdxBuf[i].offset + m_pIdxBuf[i].frameLen;
			break;
		}
	}

	dataSize  = endOffset - startOffset;

	return dataSize;
}

int CRecReadBase::GetEndOffset(uint32_t &offset,int64_t endTime)
{
	int ret = -1;
	uint32_t tmpOffset = 0;
	int64_t tmpTime = endTime;
	for(int i = 0; i <  m_maxIndex; i++)
	{
		if(m_pIdxBuf[i].tmMS > tmpTime)
		{   //校正偏移
			offset = m_pIdxBuf[i].offset;
			PrintTimeMs(m_pIdxBuf[i].tmMS,0,"Seek Idx Time");
            TRACE("Get End offset %d .\n",offset);
			return 0;
		}
#ifdef _DEBUG
		if(i < 10)
		{
			PrintTimeMs(m_pIdxBuf[i].tmMS,0,"Index Time");
		}
#endif
	}

    for(int i = m_maxIndex - 1; i > 0; i--)
    {
		if( 0 != m_pIdxBuf[i].tmMS &&
           tmpTime > m_pIdxBuf[i].tmMS )
        {

            tmpOffset = m_pIdxBuf[i].offset + m_pIdxBuf[i].frameLen ;

            if(tmpOffset < STFS_RECORD_SPACE)
            {
                offset = tmpOffset;
                TRACE("Get End offset %d .\n",offset);
            }
            else
            {
                TRACE_ERR("Get Error data offset %d ",tmpOffset);
            }

			return 0;
        }
    }
	TRACE("Get End offset %d .\n",offset);
	return ret;
}
//处理读取数据时遇到坏扇区的情况
/*
   背景知识：
    使用系统标准的接口去读取数据，遇到坏扇区时，系统会反复多次读取当前位置的数据。这样容易导致坏扇区继续扩散，
    现在有文件系统底层读取数据接口，已经考虑到此问题，所以遇到坏扇区时，立即就会停止并返回错误码，不会反复重试。

   处理思路：
    遇到坏扇区时，一般硬盘产生的并不是大面积，仅仅会是一些连续的扇区。
	理论最大4MBit码率，每秒种大概产生的数据 0.5MB
	读取过程中出现坏扇区时，考虑跳过当前I帧到下一个I帧的数据，从下一个I帧开始重新读取视频回放

   预期效果：
      例如:录像段 13：00：00 -  13：30：00
	  在回放到13：10：01时出现坏块，会跳到下秒数据 13:10:02秒开始播放，尽可能让更多录像回放出来

*/
int CRecReadBase::DealBadSector()
{
	int64_t lastReadTm          = 0;
	const uint32_t skipDataSize = STFS_GETFRM_BUFSIZE;
	uint32_t destOffset = m_curPos + skipDataSize;
	if(destOffset > m_fileLen)
	{
		return ERR_DATA_OVER_FILE;
	}

	switch(m_playMode)
	{
	//这三种回放模式都使用的正向读取，无跳帧的过程
	case    REPLAY_NORMAL:
	case	REPLAY_ONEFRAME:
	case	REPLAY_SLOW:
		{
			lastReadTm = m_pFIFO->GetNextFrmTime();
			for(int i = 0 ; i < m_maxIndex; i ++)
			{
				if(m_pIdxBuf[i].tmMS >= lastReadTm)
				{
					if(m_pIdxBuf[i].offset >= destOffset)
					{
						m_curPos = m_pIdxBuf[i].offset;
						TRACE("ch%02d NO %d cur time %lld index time %d ,offset %d \n",m_channel,i,lastReadTm,m_pIdxBuf[i].tmMS,m_pIdxBuf[i].offset);
						PrintTimeMs(lastReadTm,0,"Last read time");
						PrintTimeMs(m_pIdxBuf[i].tmMS,0,"cur Time");
						break;
					}
				}
			}
		}
		break;
	//两个快进或快退时，都已经做跳帧处理，在这里暂时还不需要做特别处理
	case    REPLAY_FAST:
		if(m_curIndex >= m_maxIndex)
		{
			TRACE_ERR("REPLAY_FAST Over curIndex %d max index %d .\n",m_curIndex,m_maxIndex);
			PrintTimeMs(m_pIdxBuf[0].tmMS,m_pIdxBuf[m_maxIndex - 1].tmMS,"I Frame area time");
			return ERR_DATA_OVER_FILE;
		}
		PrintTimeMs(m_pIdxBuf[m_curIndex].tmMS,0,"Next I Frame time");
		break;

	case    REPLAY_REBACK:
		if(m_curIndex <= 0)
		{
			TRACE_ERR("REPLAY_REBACK Over curIndex %d max index %d .\n",m_curIndex,m_maxIndex);
			PrintTimeMs(m_pIdxBuf[0].tmMS,m_pIdxBuf[m_maxIndex - 1].tmMS,"I Frame area time");
			return ERR_DATA_OVER_FILE;
		}
		PrintTimeMs(m_pIdxBuf[m_curIndex].tmMS,0,"Next I Frame time");
		break;

	default:
		break;
	}

	return 0;
}

bool CRecReadBase::IsDataEnd()
{
	if(m_bDataEnd)
	{
		return true;
	}
	return false;
}

//增加0
int CRecReadBase::MkEmptyFrame()
{
	uchar_t   frameData[128] = {0};
	FsFrame_t * pFsFrame = (FsFrame_t *)frameData;
	int headLen  = sizeof(FsFrame_t);
	int dataLen  = 64;
	int ret      = 0;

	pFsFrame->frameType = 0xFF;
	pFsFrame->frameTag  = FSFrameTag;
	pFsFrame->dataLen   = dataLen - headLen;

	TRACE("clientID %02d CH%02d MkEmpty Frame %d \n",m_clientID,m_channel,pFsFrame->frameType);
    if(NULL == m_pFIFO )
    {
#if 0
		if(m_bDownload)
		{  //下载放在第零个通道数据
			m_pFIFO = g_pIndexSet->GetFrameFIFO(m_clientID,0);
		}
		else
		{
			m_pFIFO = g_pIndexSet->GetFrameFIFO(m_clientID,m_channel);
		}
#endif
		m_pFIFO = new CReplayFrmBuf();
		m_pFIFO->Open(m_channel,SIZE_MB(1));
	}
        if(NULL != m_pFIFO)
		{
			ret = m_pFIFO->PutFrame(frameData,dataLen);
			if(m_bDownload)
			{
			//	m_pFIFO->SetDownloadEnd();
			}
		}
		else
		{
			return dataLen;
		}

		return ret;


 //   TRACE("clientID %02d CH%02d put empty Frame %d bDoownload %d FIFO CH %02d \n",
 //            m_clientID,m_channel,pFsFrame->frameType,m_bDownload,m_pFIFO->GetChannel());
	if(m_bDownload)
	{
	//	m_pFIFO->SetDownloadEnd();
	}
	ret = m_pFIFO->PutFrame(frameData,dataLen);
	return ret;
}
int CRecReadBase::MkSwitchFileFrame()
{
	uchar_t   frameData[128] = {0};
	FsFrame_t * pFsFrame = (FsFrame_t *)frameData;
	int headLen  = sizeof(FsFrame_t);
	int dataLen  = 64;
	int ret      = 0;

	pFsFrame->frameType = CFrame;
	pFsFrame->frameTag  = FSFrameTag;
	pFsFrame->dataLen   = dataLen - headLen;

	if(NULL == m_pFIFO )
	{
		if(NULL != m_pFIFO)
		{
			ret = m_pFIFO->PutFrame(frameData,dataLen);

		}
		else
		{
			return dataLen;
		}

		return ret;
	}

	ret = m_pFIFO->PutFrame(frameData,dataLen);

	return ret;
}

bool CRecReadBase::CheckFrame(uchar_t* pBuf, int64_t frTime,int len)
{
	FsFrame_t * pFrm = (FsFrame_t*)pBuf;

	if(pFrm->time != frTime)
	{
		PrintTimeMs(frTime,0,"Iframe Time");
		PrintTimeMs(pFrm->time,0,"Read Out frm");
		return false;
	}

	if(FSFrameTag != pFrm->frameTag)
	{
		return false;
	}

	return true;
}

int  CRecReadBase::CheckIdxFrame(STFS_REC_IDX * pRecIdx,uchar_t * pBuf, uint32_t bufSize)
{
	int ret = 0;
	FsFrame_t * pFrm = (FsFrame_t *)pBuf;
	if(pRecIdx->frameLen > bufSize)
	{
		return ERR_DATA_SIZE;
	}

	ret = m_pFile->ReadPos(pRecIdx->offset,pBuf,pRecIdx->frameLen);
	if(ret < 0)
	{
		return ret;
	}

	if( FSFrameTag != pFrm->frameTag)
	{
		return ERR_DATA_AREA;
	}

	if(pFrm->time != pRecIdx->tmMS)
	{
		return ERR_DATA_AREA;
	}

	if(pFrm->dataLen != pRecIdx->frameLen - FRM_HEAD_LEN )
	{
		return ERR_DATA_SIZE;
	}
	return 0;
}

//出现数据偏移不对的情况，需要及时校正
int  CRecReadBase::CorrectPos(STFS_REC_IDX & recIdx,uchar_t * pBuf,int bufSize)
{
	int ret = 0;

	if( recIdx.offset <= STFS_REC_DATA_OFFSET)
	{
		TRACE_ERR("CH%02d idx %02d offset %d .\n ",m_channel,recIdx.NO,recIdx.offset);
		return ERR_DATA_AREA;
	}
	int addOffset       = 0;
	FsFrame_t * pFrm    = NULL;
	bool bFound         = false;
	int readIdx         = 0;
	const int searchCnt = 5;
	uint32_t readPos    = 0;
    uint32_t destOffset = 0;
    uint32_t baseOffset = 0;

    if(recIdx.NO != 0)
    {
        baseOffset = m_pIdxBuf[recIdx.NO-1].offset;
    }
    else
    {
        baseOffset = STFS_REC_DATA_OFFSET;
    }

	for(readIdx = 0 ; readIdx < searchCnt; readIdx++)
	{
        readPos = baseOffset + readIdx * bufSize;
		ret = m_pFile->ReadPos(readPos,pBuf,bufSize);
		if(ret < 0)
		{
			TRACE_ERR("CH%02d idx %02d offset %d .\n ",m_channel,recIdx.NO,recIdx.offset);
			return ret;
		}

		//找到准备的偏移量
		addOffset = bufSize * readIdx;
		while(( addOffset + FRM_HEAD_LEN) < bufSize)
		{
			pFrm = (FsFrame_t*)(pBuf + addOffset);
			if( recIdx.tmMS		 == pFrm->time     &&
				recIdx.frameType == pFrm->frameType)
			{
				CROSS_ASSERT(FSFrameTag == pFrm->frameTag);
				TRACE("Correct offset %d  .\n",addOffset);
				bFound     = true;
				destOffset =  baseOffset + addOffset;
				goto CORRECT_END;
			}
			addOffset++;
		}
	}

//校正结束
CORRECT_END:
	//没有找到
	if(!bFound)
	{
		TRACE("CH%02d Correct fail offset %d  .\n",m_channel,addOffset);
		return ERR_DATA_AREA;
	}

	//校正索引偏移
	int correctCnt = 0;
	int diffOffset = destOffset - recIdx.offset;
	for(uint32_t idx = recIdx.NO; idx < m_maxIndex;idx++)
	{
		if( m_pIdxBuf[idx].tmMS      != 0 &&
			m_pIdxBuf[idx].isCorrect != STFS_REC_IDX_CORRECTED) //没有校正过
		{
			m_pIdxBuf[idx].offset   += diffOffset;
			m_pIdxBuf[idx].isCorrect = STFS_REC_IDX_CORRECTED; //索引偏移被校正
			correctCnt++;
		}
	}

	TRACE("Correct idx %d .\n",correctCnt);
	return 0;
}

int CRecReadBase::GetRecIdxByHeadBuf()
{
	int indexLen = STFS_REC_DATA_OFFSET - STFS_REC_IDX_OFFSET;
	int maxNum   = indexLen/REC_INDEX_LEN;
	if(indexLen > (int)(STFS_REC_DATA_OFFSET - STFS_REC_IDX_OFFSET))
	{	//
		delete [] m_pIdxBuf;
		m_pIdxBuf = new STFS_REC_IDX[maxNum];
		TRACE_ERR("CH%02d Re-new buf info.\n",m_channel);
	}

	uint32_t gpsNum = m_frmIdx.GetGPSInfoNum();
	if(gpsNum > (m_gpsBufSz/GPS_INDEX_LEN))
	{
		delete [] m_pGpsBuf;
		m_pGpsBuf = new STFS_GPS_IDX[gpsNum];
		m_gpsBufSz = gpsNum * GPS_INDEX_LEN;
		TRACE_ERR("CH%02d Re-new gps info size %d.\n",m_channel,m_gpsBufSz);
	}

	m_maxIndex = 0;
	//由于索引与gps是混合在一起的
	m_maxIndex = m_frmIdx.GetFrmIdxData(m_pIdxBuf,maxNum);
	m_gpsNum   = m_frmIdx.GetGpsIdxData(m_pGpsBuf,maxNum);

	return m_maxIndex;
}

//实时检测数据是否已经越界
int  CRecReadBase::CheckFrameArea(uint32_t curDataPos)
{
	for(int i = m_curIndex ; i < m_maxIndex; i++)
	{
		if( m_pIdxBuf[i].offset >= curDataPos)
		{
			if(m_pIdxBuf[i].tmMS > m_endTime)
			{
				m_curIndex = i;
				return ERR_DATA_OVER_FILE;
			}
			else
			{
				return 0;
			}
		}
	}

	return ERR_PARAM;
}

int  CRecReadBase::ReadPos(uint32_t offset, uchar_t * pBuf, uint32_t dataSize)
{
	if( NULL == m_pFile)
	{
		return ERR_FILE_NOOPEN;
	}
	return m_pFile->ReadPos(offset,pBuf,dataSize);
}

int CRecReadBase::Seek(uint32_t offset)
{
	if( NULL == m_pFile)
	{
		return ERR_FILE_NOOPEN;
	}
	m_pFile->Seek(offset,SEEK_SET);

	return 0;
}

int  CRecReadBase::Read(uchar_t * pBuf, uint32_t dataSize)
{

	return m_pFile->Read(pBuf,dataSize);
}

uchar_t *   CRecReadBase::GetReplayTmpBuf(uint32_t & bufSize)
{
	bufSize = g_rec_dataIO.GetRpBufSize(m_pFile->get_device_name());
	return g_rec_dataIO.GetRpBuf(m_pFile->get_device_name());
}

typedef union {               					// Coded byte, ASF or RTP format
	uint8_t data;
	struct {
		uint8_t bit01:2;
		uint8_t bit23:2;
		uint8_t bit45:2;
		uint8_t bit67:2;
	};
} speech_byte_t;

uint8_t CRecReadBase::convert_asf_to_rtp_byte(uint8_t asfbyte)
{
	speech_byte_t spbyte1,spbyte2;

	spbyte1.data = asfbyte;
	spbyte2.bit01 = spbyte1.bit67;
	spbyte2.bit23 = spbyte1.bit45;
	spbyte2.bit45 = spbyte1.bit23;
	spbyte2.bit67 = spbyte1.bit01;

	return (spbyte2.data);
}

void CRecReadBase::convert_asf_to_rtp_frame(uint8_t *srcbuf, uint8_t *dstbuf, uint32_t flen)
{
	uint32_t i;

	for ( i = 0; i < flen; i++)
		dstbuf[i] = convert_asf_to_rtp_byte(srcbuf[i]);
}

int PUB_THREAD_CALL CRecReadBase::ReadFileThd(void * arg)
{
	CRecReadBase* pTask = (CRecReadBase * )arg;
	int ret = 0;
	static int bRun = false;
	if(bRun)
	{
		return -1;
	}

	bRun = true;
	while (true)
	{
		ret = pTask->Read_TO_FIFO(30);
		if(ret < 0)
		{
			break;
		}
		CrossSleep(60);
	}
	bRun = false;
	return 0;
}
